// type的几种特殊写法

// keyof 一个有2个对象组成联合类型时，新类型是由共有的字符串属性名，所组成的联合类型
type O1 = { name: "张三", age: 18 }
type O2 = { name: "李四", address: "广州" }
type O3 = keyof (O1 | O2)  // type O3 = "name"

// 定义含有方法的对象，与定义方法
type TestType1 = { say: (...args: any) => any }  // 表示一个对象里有个say方法的类型
type TestType2 = { (...args: any): any }  // 表示这个方法类型的本身的类型
interface TestType3 { (...args: any): any }  // 表示这个方法类型的本身的接口

// 泛型工厂函数类型，在type与interface中表示也是同理
type TestType4 = { new (...args: any): any }
interface TestType5 {  new (...args: any): any }
// 等同与
type TestType6 = new (...args: any) => any

// 让TS 默认展开对象类型中的属性名类型与属性值类型
// TypeScript IDE 默认不展开 keyof T 的结果，显示为 keyof T 的别名（如 keyof any[]）
// 强制展开方法：可以通过工具类型（如 Expand）强制显示展开的联合类型。
type Convert<T> = T extends any ? T : never;
type ShowArrayType_ = Convert<keyof Array<any>>
type ShowArrayType = Expand<keyof Array<any>>


// 不同类型直接的赋值
// 子类型 可以赋值给 父类型。因为子类型中有父类型所有的属性与方法，反之则不能赋值

// ### 对象直接的赋值
let O1: object = { name: "张三", age: 18 }
let O2: object = { address: "广州" }
O2 = O1  // 因为此时O1 与 O2 的类型都是 object，type O1 = object
// O1.name  // 会报错。虽然O1对象中有name，但O1的类型是object类型，原始的Object是没该属性

let O3 = { name: "张三", age: 18 }
let O4 = {  address: "广州" }
// O3 = O4  // 会报错。因为type O3 = { name: string, age: number }与O4的类型不兼容。O4中没有对应属性

// ### 类的直接赋值
// 1、要么等号右边的是子类。因为子类拥有父类的属性与方法，可以赋值。
// 2、要么等号右边的是一个拥有同样属性与方法的，另一个类。
class Human { name!: string; age!: number; }
class Man { name!: string }

let ManCopy = Man  // 类是无法直接被赋值的，要先定义一个变量来接收这个类，让 TS 知道你此时把这个类当做变量了，而不是当做类构造函数来使用。
ManCopy = Human  // 也就是说无法 Man = Human，因为类无法被直接赋值。

// 如果方法参数的类型为any，即使该参数接收了父类，且有子类继承了该变量，该子类也无法赋值给父类了。
// 因为参数的类型any 与 父类的类型(new (...args): 父类名)不兼容。但是通用构造函数类型({ new (...args: any): any })则与父类兼容。
// 所以函数参数的类型要定义继承于通用构造函数类型的泛型T，因为泛型是定义是不确定类型，调用时（使用时）确定类型的语法机制。
class Man2 { name!: string }
let Man2Son = (function (targetClass: any) {
  class Man2Son extends targetClass {
    constructor(...args: any) {
      super(...args)
    }
  }
  return Man2Son
})(Man2)
let ManCopy2 = Man2
// ManCopy2 = Man2Son  // 会报错。因为子类Man2Son继承的targetClass的类型是any，不是 Man2的类型。
// 如果改为泛型T，T在extends通用构造函数类型({ new (...args: any): any }后就不会报错了。
// 因为泛型是定义是不确定类型，调用时（使用时）确定类型的语法机制。
export {}